<template>
    <div >

      <div
      class="perChat"
      :style="'transform:translate('+x+'px,'+y+'px);'+'width:'+width+'px;height:'+height+'px'"
        id="AiHumanDiv"
        ref="AiHumanDivRef"
      ></div>
  
      <audio
        id="rtcAudio"
        autoplay="true"
      ></audio>
      <video
        v-show="isShowVideo"
        id="rtcVideo"
        style="width:540px; float: left; background-color: blue;"
        autoplay="true"
        playsinline="true"
        loop
      ></video>
    </div>
</template>
<script >
  import * as THREE from 'three';
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
  import { Resizer } from './scripts/Resizer.js'
  import { toRaw } from 'vue';
  import {mittTypes} from "@/util/types"
  var scene,camera,renderer,texture,geometry,light,backerColor,material,mesh,controls
  export default {
    name: 'aiHuman2dRenderer',
    props: {
        x: {
        type: String,
        default: '0'
        },
        y: {
        type: String,
        default: '0'
        },
        width: {
        type: String,
        default: '540'
        },
        height: {
        type: String,
        default: '960'
        },
        stream_id:{
          type:String,
          default:null
        },
        human2dConfig:{
          type:Object,
          default:null
        }
    },
    data() {
      return {
        // changeBg: changeBg,
  
        isShowVideo: false,
        rtcIce: null,
        startBtnShow: true,
  
        text: '你好，我是数字人',
        // camera: null,
        // scene: null,
        // renderer: null,
        rtcVideo: null,
        texture: null,
        material: null,
        geometry: null,
        mesh: null,
        light: null,
        humanWidth: 540,
        humanHeight: 960,
        canDrive: false,
      };
    },
    computed: {
    },
    created() {
  
  
    },
    methods: {
      showVideo() {
        this.isShowVideo = !this.isShowVideo;
      },
  
  
      negotiate() {
        this.rtcIce.addTransceiver('video', { direction: 'recvonly' });
        this.rtcIce.addTransceiver('audio', { direction: 'recvonly' });
        //输出当前时间
        console.log('negotiate')
        let time = new Date().getTime();
        return this.rtcIce.createOffer().then((offer) => {
          let time1 = new Date().getTime();
          console.log('negotiate time', (time1 - time));
          return this.rtcIce.setLocalDescription(offer);
        }).then(() => {
          // wait for ICE gathering to complete
          let time2 = new Date().getTime();
          console.log('negotiate time2', (time2 - time));
          return new Promise((resolve) => {
            
            if (this.rtcIce.iceGatheringState === 'complete') {
              let time21 = new Date().getTime();
              console.log('negotiate time21', (time21 - time));
              resolve();
            } else {
              const timeout =setTimeout(()=>{
                console.warn("Ice gathering timeout, proceeding...");
                resolve()
              },5000);// 2 秒超时
              const checkState = () => {
                let time22 = new Date().getTime();
                console.log('negotiate time22', (time22 - time));
               
                if (this.rtcIce.iceGatheringState === 'complete') {
                  this.rtcIce.removeEventListener('icegatheringstatechange', checkState);
                  clearTimeout(timeout);
                  let time23 = new Date().getTime();
                  console.log('negotiate time23', (time23 - time));
                  resolve();
                }
              };
              this.rtcIce.addEventListener('icegatheringstatechange', checkState);
            }
          });
        }).then(() => {
          var offer = this.rtcIce.localDescription;
          let time3 = new Date().getTime();
          console.log('negotiate time3', (time3 - time));
          return fetch(
            // 'http://llm.genemodel.com:8010/offer',
            // 'http://172.20.0.246:18086/offer',
            this.stream_id+'perApi/offer',
             //http://per.aihuman.huilan.com:18086/offer',
            
            {
              body: JSON.stringify({
                sdp: offer.sdp,
                type: offer.type,
              }),
              headers: {
                'Content-Type': 'application/json'
              },
              method: 'POST'
            });
        }).then((response) => {
          let time4 = new Date().getTime();
          console.log('negotiate time4', (time4 - time));
          return response.json();
        }).then((answer) => {
          this.canDrive = true;
          return this.rtcIce.setRemoteDescription(answer);
        }).catch((e) => {
          this.canDrive = false;
          this.$message.error('webrtc:' + e.toString())
        });
      },
  
  
      start() {
        
      },
  
      stop() {
        // document.getElementById('stop').style.display = 'none';
        // document.getElementById('start').style.display = 'inline-block';
        this.startBtnShow = true;
        // close peer connection
        setTimeout(() => {
          this.canDrive = false;
          this.rtcIce.close();
        }, 500);
      },
  
      driveRtc(answer) {
        console.log('answer', answer);
        this.text = answer;
        if (this.canDrive) {
          fetch('/perApi/human', {
            body: JSON.stringify({
              text: answer,
              type: 'echo',
              interrupt: true,
            }),
            headers: {
              'Content-Type': 'application/json'
            },
            method: 'POST'
          }).then((response) => {
            console.log('response', response)
            this.$emit('handleChatReply');
          });
        } else {
          this.$emit('handleChatReply');
        }
  
  
      },
  
  
      AiHuman_init() {
        var container = document.getElementById('AiHumanDiv');
        
        // camera = new THREE.PerspectiveCamera(45, this.humanWidth / this.humanHeight, 1, 10000);
        // camera.position.z = 10;
        // camera.position.x = 20;
        // camera.position.y = 0;

        // 正投影相机
        const k = container.clientWidth / container.clientHeight; //canvas画布宽高比
        const s = 10//控制left, right, top, bottom范围大小
        camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 100);
        camera.position.set(0, 0, 13)
  
        scene = new THREE.Scene();
  
        this.light = new THREE.DirectionalLight(0xffffff, 3);
        this.light.position.set(0.5, 1, 1).normalize();
        scene.add(this.light);
  
        renderer = new THREE.WebGLRenderer({
          antialias: true, //开启锯齿
          alpha: true,
        });
        renderer.setPixelRatio(window.devicePixelRatio);
        // renderer.setSize(this.humanWidth, this.humanHeight);

        container.appendChild(renderer.domElement);
  
       //创建尺寸适配
       var resizer = new Resizer(
          container,
          camera,
          renderer
        )
  
        // this.rtcVideo = document.getElementById('videoTest');
        this.rtcVideo = document.getElementById('rtcVideo');
  
        let _this = this;
        this.rtcVideo.addEventListener("play", function(){
          _this.emitter.emit(mittTypes.LOADING.LOAD_FINISH)
          _this.emitter.emit(mittTypes.LOADING.UPDATE_LOADING_WORD,'数字人已启动，请点击按钮开始')
        });
        _this.emitter.on(mittTypes.HUMAN2D.PLAY,function(){
          _this.rtcVideo.play();
        })
        texture = new THREE.VideoTexture(this.rtcVideo);
        texture.colorSpace = THREE.SRGBColorSpace;
  
        geometry = new THREE.PlaneGeometry(9, 16);
  
        geometry.scale(1, 1, 1);
  
  
        // geometry1 = new THREE.BoxGeometry(1, 1, 1);
        // material1 = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        // this.cube1 = new THREE.Mesh(geometry1, material1);
        // scene.add(this.cube1);
  
        // camera.position.z = 5;
  
  
        // material = new THREE.MeshBasicMaterial( { map: texture } );
        var backerColor = new THREE.Color("rgb(77, 208, 76)");
  
        // var backerColor = new THREE.Color("rgb(73, 137, 69)");
  
        // material = new THREE.ShaderMaterial({
        //   uniforms: {
        //     tex: { value: texture },
        //     texWidth: { value: this.humanWidth },
        //     texHeight: { value: this.humanHeight },
        //     keyColor: { value: backerColor },
        //     similarity: { value: 0.30 }, // 进一步降低相似度
        //     smoothness: { value: 0.06 }, // 减小平滑度
        //     spill: { value: 0.55 },      // 进一步增加溢出控制强度
        //   },
          
        //   // 顶点着色器保持不变
        //   vertexShader: `
        //     varying vec2 vUv;
        //     void main() {
        //       vUv = uv;
        //       gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        //     }
        //   `,
          
        //   // 修改片段着色器
        //   fragmentShader: `
        //     precision mediump float;
        //     uniform sampler2D tex;
        //     uniform float texWidth;
        //     uniform float texHeight;
        //     const vec3 keyColor = vec3(0.47, 1.0, 0.47);
        //     uniform float similarity;
        //     uniform float smoothness;
        //     uniform float spill;
        //     varying vec2 vUv;
            
        //     vec2 RGBtoUV(vec3 rgb) {
        //       return vec2(
        //         rgb.r * -0.169 + rgb.g * -0.331 + rgb.b *  0.5    + 0.5,
        //         rgb.r *  0.5   + rgb.g * -0.419 + rgb.b * -0.081  + 0.5
        //       );
        //     }
            
        //     vec4 GetRgba(float detaX, float detaY) {
        //       vec2 texCoord = vec2(
        //         (gl_FragCoord.x + detaX) / texWidth,
        //         (gl_FragCoord.y + detaY) / texHeight
        //       );
        //       vec4 rgba = texture2D(tex, vUv);
        //       float green = rgba.g - max(rgba.r, rgba.b);
        //       float greenKey = keyColor.g - max(keyColor.r, keyColor.b);
              
        //       // 使用更精确的边缘检测
        //       float mask = smoothstep(greenKey - similarity, greenKey - similarity + smoothness, green);
        //       rgba.a = 1.0 - mask; // 反转遮罩
              
        //       // 边缘绿色处理 - 直接在获取RGBA时处理
        //       if (rgba.a > 0.05 && rgba.a < 0.95) {
        //         // 如果是边缘区域且绿色占主导，降低绿色通道
        //         if (rgba.g > rgba.r * 1.05 && rgba.g > rgba.b * 1.05) {
        //           // 根据透明度动态调整绿色抑制强度
        //           float greenReduction = mix(0.85, 0.95, rgba.a);
        //           rgba.g *= greenReduction;
        //         }
        //       }
              
        //       return rgba;
        //     }
            
        //     // 增强的颜色校正函数
        //     vec3 correctColor(vec3 color, float spillAmount) {
        //       // 检测是否为暗色区域
        //       float luminance = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;
              
        //       // 处理绿色溢出 - 更激进地去除绿色
        //       if (color.g > color.r && color.g > color.b) {
        //         float excess = color.g - max(color.r, color.b);
        //         color.g = max(color.r, color.b) + excess * (1.0 - spillAmount);
                
        //         // 额外的绿色校正 - 更强力地降低绿色通道
        //         color.g *= 0.95;
        //       }
              
        //       // 增强暗色区域对比度
        //       if (luminance < 0.3) {
        //         color.rgb *= 0.95; // 加深暗色区域
                
        //         // 对暗区的绿色进行额外处理
        //         if (color.g > color.r * 1.02 && color.g > color.b * 1.02) {
        //           // 更激进地降低暗区绿色
        //           color.g = (color.r + color.b) * 0.48;
        //         }
        //       }
              
        //       // 整体微调色彩平衡 - 增加红色和蓝色以抵消绿色
        //       color.r *= 1.03;
        //       color.b *= 1.03;
              
        //       return color;
        //     }
            
        //     // 边缘增强函数 - 专门处理边缘绿线
        //     vec4 enhanceEdges(vec4 rgba) {
        //       // 只处理半透明区域(边缘)
        //       if (rgba.a > 0.05 && rgba.a < 0.95) {
        //         // 检测绿色边缘
        //         float greenDominance = rgba.g / max(max(rgba.r, rgba.b), 0.001);
                
        //         if (greenDominance > 1.0) {
        //           // 绿色占主导，根据绿色主导程度和透明度调整
        //           float edgeFactor = (1.0 - rgba.a) * min(greenDominance - 1.0, 0.5) * 2.0;
                  
        //           // 降低绿色，增加红蓝
        //           rgba.g *= (1.0 - edgeFactor * 0.3);
        //           rgba.r *= (1.0 + edgeFactor * 0.1);
        //           rgba.b *= (1.0 + edgeFactor * 0.1);
                  
        //           // 对于非常明显的绿边，可以稍微调整透明度
        //           if (greenDominance > 1.3 && rgba.a < 0.7) {
        //             rgba.a *= 0.95;
        //           }
        //         }
        //       }
              
        //       return rgba;
        //     }
            
        //     vec4 ProcessChromaKey() {
        //       vec4 rgba = GetRgba(0.0, 0.0);
              
        //       // 边缘处理
        //       if (rgba.a > 0.05 && rgba.a < 0.95) {
        //         vec4 rgbas[9];
        //         float maxMask = 0.0;
        //         vec4 minRgba = rgba;
                
        //         // 3x3 像素矩阵采样
        //         for (int i = -1; i <= 1; i++) {
        //           for (int j = -1; j <= 1; j++) {
        //             vec4 tmp;
        //             if (i == 0 && j == 0) {
        //               tmp = rgba;
        //             } else {
        //               tmp = GetRgba(float(i), float(j));
        //             }
        //             rgbas[(i+1)*3 + (j+1)] = tmp;
        //             if (tmp.a > maxMask) {
        //               maxMask = tmp.a;
        //               minRgba = tmp;
        //             }
        //           }
        //         }
                
        //         rgbas[4] = minRgba;
        //         rgbas[4].a = rgba.a;
        //         vec4 dest = vec4(0.0, 0.0, 0.0, 0.0);
                
        //         // 颜色平滑处理
        //         float gs[9];
        //         gs[0] = gs[2] = gs[6] = gs[8] = 0.05;
        //         gs[1] = gs[3] = gs[5] = gs[7] = 0.05;
        //         gs[4] = 0.6;
        //         for(int i = 0; i < 9; i++) {
        //           dest.rgb = dest.rgb + rgbas[i].rgb * gs[i];
        //         }
                
        //         // 透明度平滑处理
        //         gs[0] = gs[2] = gs[6] = gs[8] = 0.05;
        //         gs[1] = gs[3] = gs[5] = gs[7] = 0.15;
        //         gs[4] = 0.2;
        //         for(int i = 0; i < 9; i++) {
        //           dest.a = dest.a + rgbas[i].a * gs[i];
        //         }
                
        //         rgba.rgb = dest.rgb;
        //         rgba.a = dest.a;
                
        //         // 边缘区域应用更强的颜色校正
        //         rgba.rgb = correctColor(rgba.rgb, spill);
                
        //         // 边缘额外处理 - 降低半透明区域的绿色
        //         if (rgba.a > 0.1 && rgba.a < 0.9 && rgba.g > rgba.r && rgba.g > rgba.b) {
        //           rgba.g *= (0.92 - (0.08 * (1.0 - rgba.a)));
        //         }
                
        //         // 应用边缘增强处理
        //         rgba = enhanceEdges(rgba);
        //       } else {
        //         // 非边缘区域也应用颜色校正，但强度较低
        //         rgba.rgb = correctColor(rgba.rgb, spill * 0.7);
        //       }
              
        //       // 预乘alpha
        //       rgba.rgb = rgba.rgb * rgba.a;
              
        //       return rgba;
        //     }
            
        //     void main(void) {
        //       gl_FragColor = ProcessChromaKey();
        //     }
        //   `,
          
        //   transparent: true,
        // });

        //绿幕视频处理的逻辑
        //处理视频纹理
        var bkcolor = new THREE.Color("rgb(77, 208, 76)") // console.log("color:",bkcolor);
        var threshold = 0.58
        var pureGreen = 1
        var blockAlpha = 0.3
        var uniforms = {
          time: {
            value: 1.0
          },
          pointTexture: {
            value: texture
          },
          backColor: {
            value: bkcolor
          },
          u_threshold: {
            value: threshold
          },
          u_pureGreen: {
            value: pureGreen
          },
          u_blockAlpha: {
            value: blockAlpha
          }
        }
        material = new THREE.ShaderMaterial({
          uniforms: uniforms,
          vertexShader: `varying vec2 vUv;
                      void main() {
                      vUv = uv;
                      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                      }`,
          fragmentShader: `#ifdef GL_ES
                      precision highp float;
                      #endif
        
                  uniform float time;
                  uniform sampler2D pointTexture;
                  uniform vec3 backColor;
                  varying vec2 vUv;
                  uniform float u_threshold;
                  uniform float u_pureGreen;
                  uniform float u_blockAlpha;
                  float u_clipBlack=0.2;
                  float u_clipWhite=1.;
        
                  float rgb2cb(float r, float g, float b){ return 0.5 + -0.168736*r - 0.331264*g + 0.5*b; } 
                  float rgb2cr(float r, float g, float b){ return 0.5 + 0.5*r - 0.418688*g - 0.081312*b; } 
                  float smoothclip(float low, float high, float x){ if (x <= low){ return 0.0; } if(x >= high){ return 1.0; } return (x-low)/(high-low); }
                  vec4 greenscreen(vec4 colora, float Cb_key,float Cr_key, float tola,float tolb, float clipBlack, float clipWhite)
                  { 
                      float cb = rgb2cb(colora.r,colora.g,colora.b); 
                      float cr = rgb2cr(colora.r,colora.g,colora.b); 
                      float alpha = distance(vec2(cb, cr), vec2(Cb_key, Cr_key)); 
                      alpha = smoothclip(tola, tolb, alpha); 
                      float r = max(gl_FragColor.r - (1.0-alpha)*backColor.r, 0.0); 
                      float g = max(gl_FragColor.g - (1.0-alpha)*backColor.g, 0.0); 
                      float b = max(gl_FragColor.b - (1.0-alpha)*backColor.b, 0.0); 
                      if(alpha < clipBlack){ alpha = r = g = b = 0.0; } 
                      if(alpha > clipWhite){ alpha = 1.0; } 
                      if(clipWhite < 1.0){ alpha = alpha/max(clipWhite, 0.9); } 
                      return vec4(r,g,b, alpha); 
                  }
                  void main( void ) {
        
                      gl_FragColor = vec4(texture2D(pointTexture, vUv).rgb, 1);
                      vec4 oldcl=gl_FragColor;
                      float tola = 0.01; 
                      float tolb = u_threshold/2.0; 
                      float cb_key = rgb2cb(backColor.r, backColor.g, backColor.b); 
                      float cr_key = rgb2cr(backColor.r, backColor.g, backColor.b); 
                      gl_FragColor = greenscreen(gl_FragColor, cb_key, cr_key, tola, tolb, u_clipBlack, u_clipWhite);
                      if(gl_FragColor.a>u_blockAlpha&&u_pureGreen<0.8)
                        gl_FragColor=vec4(oldcl.rgb,1);

        

                  }`,
          transparent: true
        })
  

        // material = new THREE.ShaderMaterial({
  
        //   uniforms: {
        //     // 'videoTexture': {
        //     // 	value: texture
        //     // },
        //     // time: {
        //     // 	value: 1
        //     // },
        //     pointTexture: {
        //       value: texture
        //     },
        //     backColor: {
        //       value: backerColor
        //     },
        //     u_threshold: {
        //       value: 0.21
        //     }
        //   },
  
        //   vertexShader: `
        //   varying vec2 vUv;
        //   void main() {
        //     vUv = uv;
        //     gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        //   }`,
  
        //   fragmentShader: `
        //   #ifdef GL_ES
        //   precision highp float;
        //   #endif
        //   uniform float time;
        //   uniform sampler2D pointTexture;
        //   uniform vec3 backColor;
        //   varying vec2 vUv;
        //   uniform float u_threshold;
        //   float u_clipBlack=0.66;
        //   float u_clipWhite=4.5;
        //   float rgb2cb(float r, float g, float b){ return 0.5 + -0.168736*r - 0.331264*g + 0.5*b; } 
        //   float rgb2cr(float r, float g, float b){ return 0.5 + 0.5*r - 0.418688*g - 0.081312*b; } 
        //   float smoothclip(float low, float high, float x){ if (x <= low){ return 0.0; } if(x >= high){ return 1.0; } return (x-low)/(high-low); }
        //   vec4 greenscreen(vec4 colora, float Cb_key,float Cr_key, float tola,float tolb, float clipBlack, float clipWhite)
        //   {
        //      float cb = rgb2cb(colora.r,colora.g,colora.b); 
        //      float cr = rgb2cr(colora.r,colora.g,colora.b);
        //      float alpha = distance(vec2(cb, cr), vec2(Cb_key, Cr_key));
        //      alpha = smoothclip(tola, tolb, alpha);
        //      float r = max(gl_FragColor.r - (1.0-alpha)*backColor.r, 0.0);
        //      float g = max(gl_FragColor.g - (1.0-alpha)*backColor.g, 0.0);
        //      float b = max(gl_FragColor.b - (1.0-alpha)*backColor.b, 0.0);
        //      if(alpha < clipBlack){ alpha = r = g = b = 0.0; } 
        //      if(alpha > clipWhite){ alpha = 1.0; } 
        //      if(clipWhite < 1.0){ alpha = alpha/max(clipWhite, 0.9); } 
        //      return vec4(r,g,b, alpha); 
        //     }
        //     void main( void ) {
        //       gl_FragColor = vec4(texture2D(pointTexture, vUv).rgb, 1);
        //       float tola = 0.0; 
        //       float tolb = u_threshold/2.0;
        //       float cb_key = rgb2cb(backColor.r, backColor.g, backColor.b);
        //       float cr_key = rgb2cr(backColor.r, backColor.g, backColor.b);
        //       gl_FragColor = greenscreen(gl_FragColor, cb_key, cr_key, tola, tolb, u_clipBlack, u_clipWhite);
        //     }`,
        //   // side: THREE.DoubleSide,
        //   transparent: true,
  
        // });
  
  
  
        mesh = new THREE.Mesh(geometry, material);
        mesh.position.y = 0;
        mesh.position.x = 0
        mesh.position.z = 0;
        scene.add(mesh);
  
  
        // window.addEventListener('resize', AiHuman_onWindowResize);
  
        controls = new OrbitControls(camera, renderer.domElement);
        controls.enableRotate = false;
        controls.minDistance = 1;
        controls.maxDistance = 200;
        
  
      },
  
  
      AiHuman_animate() {
        requestAnimationFrame(this.AiHuman_animate);
        renderer.render(toRaw(scene), toRaw(camera));
      },
  
      handleVideo() {
        this.AiHuman_init();
        this.AiHuman_animate();
      },
    },
    mounted() {
        const config = {
         sdpSemantics: 'unified-plan',
         iceServers: [{ 
          "urls": this.human2dConfig.urls, 
          "username": this.human2dConfig.username, 
          "credential": this.human2dConfig.credential }],
          iceTransportPolicy:"relay"//制使用 TURN，避免 P2P 失败后长时间等待
        };
      //   const config = {
      //   sdpSemantics: 'unified-plan',
      //   iceServers: [{ "urls": "turn:aihuman.huilan.com:19303", "username": "PixelStreamingUser", "credential": "AnotherTURNintheroad" }],
      // };
  
        console.log(config)
        // if (document.getElementById('use-stun').checked) {
        // config.iceServers = [{ urls: ['stun:stun.l.google.com:19302'] }];
        // config.iceServers = [{ urls: ['stun:aihuman.huilan.com:19313'] }];
        // config.iceServers = [{ "urls": "turn:aihuman.huilan.com:19313", "username": "PixelStreamingUser", "credential": "AnotherTURNintheroad" }];
        // }
  
        this.rtcIce = new RTCPeerConnection(config);
  
        // connect audio / video
        this.rtcIce.addEventListener('track', (evt) => {
          if (evt.track.kind == 'video') {
            document.getElementById('rtcVideo').srcObject = evt.streams[0];
          } else {
            document.getElementById('rtcAudio').srcObject = evt.streams[0];
          }
        });
  
        // document.getElementById('start').style.display = 'none';
        // document.getElementById('stop').style.display = 'inline-block';
  
        this.negotiate();
        this.startBtnShow = false;
  
        this.AiHuman_init();
        this.AiHuman_animate();
  
  
    },
    beforeDestroy() {
  
    },
  
  };
  </script>
  <style lang="scss">
  .perChat {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
  }
  </style>